#!/usr/bin/env perl
#
# Copyright (C) 2003, 2011 Dan McMahill
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.


# This script is used to process PADS PowerPCB .eco files to backannotate
# changes to gEDA schematics.  Not all ECO file sections are implemented,
# and this program is relatively untested.  Please proceed with caution.
#

# for parsing input options
use Getopt::Long;

# for ceil function
use POSIX;

# don't allow -he to be interpreted as --help
$Getopt::Long::autoabbrev=0;

# make the options be case sensitive
$Getopt::Long::ignorecase=0;

&GetOptions( ('h|help' => \&usage,
	      'n|nocopy' => \$nocopy,
	      'v|verbose' => \$verbose,
	      'V|version' => \&version
	     ) );

usage() if $Getopt::Long::error;
usage() unless @ARGV;

# ECO file name
$eco = shift( @ARGV );

# if no schematic names follow, exit
usage() unless @ARGV;

# make sure the input netlist exists and we can open it
$i = 0;
while( @ARGV ) {
  $fname[$i] = shift( @ARGV );
  die "Schematic file $fname[$i] does not exist or can not be read"
    unless -r $fname[$i];
  $i++;
}

$filecnt = $i;


if( $verbose ){ print "Loading ECO file: $eco\n"; }
$eco_state="DEFAULT";
$got_end=0;
open( ECO, "$eco" ) or die "Can't open ECO file $eco !\n";

$line = <ECO>;
if( $line =~ /^\*PADS-ECO-V3\.0\*[\n\r]*$/ ) {
    print "Read ECO header line\n" if $verbose ;
} elsif( $line =~ /^\*PADS-ECO-\*/ ) {
    print "Unknown ECO file version: $line\n";
    exit 1;
} else {
    print "Invalid ECO file header: $line\n";
    exit 1;
}

while( $line = <ECO> ) {
    #  if( $verbose ){ print "$line\n"; }
    if( $line =~ /^\*CHGPART\*/ ) {
	# change type of part (forward annotation)
	$eco_state="CHGPART";
	next;
    } elsif( $line =~ /^\*DEL_ATTRIBUTE\*/ ) {
	# delete object attribute (forward annotation)
	$eco_state="DEL_ATTRIBUTE";
	next;
    } elsif( $line =~ /^\*DELPART\*/ ) {
	# delete part from the design (forward annotation)
	$eco_state="DELPART";
	next;
    } elsif( $line =~ /^\*DELPIN\*/ ) {
	# delete pins from net (forward annotation)
	$eco_state="DELPIN";
	next;
    } elsif( $line =~ /^\*END\*/ ) {
	# end of ECO file
	$eco_state="END";
	$got_end=1;
	next;
    } elsif( $line =~ /^\*JOINNET\*/ ) {
	# join two nets together (forward annotation)
	$eco_state="JOINNET";
	next;
    } elsif( $line =~ /^\*NET\*/ ) {
	# add pin to net (forward annotation)
	$eco_state="NET";
	next;
    } elsif( $line =~ /^\*PART\*/ ) {
	# add part to the design (forward annotation)
	$eco_state="PART";
	next;
    } elsif( $line =~ /^\*REMARK\*/ ) {
	# don't change state on comment lines
	print "Skipping *REMARK* line\n" if $verbose;
	next;
    } elsif( $line =~ /^\*RENNET\*/ ) {
	# rename netname (forward annotation)
	$eco_state="RENNET";
	next;
    } elsif( $line =~ /^\*RENPART\*/ ) {
	# rename part in design (forward/backward annotation)
	$eco_state="RENPART";
	print "Starting *RENPART* (Refdes Renumber) section\n" if $verbose;
	next;
    } elsif( $line =~ /^\*SET_ATTRIBUTE\*/ ) {
	# set object attribute (forward annotation)
	$eco_state="SET_ATTRIBUTE";
	next;
    } elsif( $line =~ /^\*SIGNAL\*/ ) {
	# part of *NET* and also *SPLITNET* (forward annotation)
	$eco_state="SIGNAL";
	next;
    } elsif( $line =~ /^\*SPLITNET\*/ ) {
	# split net into two new nets (forward annotation)
	$eco_state="SPLITNET";
	next;
    } elsif( $line =~ /^\*SWPGATES\*/ ) {
	$eco_state="SWPGATES";
	print "Starting *SWPGATES* (Gate Swapping) section\n" if $verbose;
	next;
    } elsif( $line =~ /^\*SWPPINS\*/ ) {
	$eco_state="SWPPINS";
	print "Starting *SWPPINS* (Pin Swapping) section\n" if $verbose;
	next;
    } elsif( $line =~ /^\*/ ) {
	print "WARNING:  Unknown command line:\n";
	print "          $line\n";
	$eco_state="DEFAULT";
	next;
    } else {
	# this must be a data line
	#if( $verbose ){ print "Processing data line: $line"; }
    }

    if( $eco_state =~ "RENPART" ) {
	parseRENPART($line);
    } elsif( $eco_state =~ "SWPGATES" ) {
	parseSWPGATES($line);
    } elsif( $eco_state =~ "SWPPINS" ) {
	parseSWPPINS($line);
    } else {
	print "Skipping lines in $eco_state section\n" if $verbose;
    }

}
close( ECO );

for($i=0; $i < $filecnt; $i++) {
  print "Processing schematic file #", $i+1, ": $fname[$i]\n";
  open(NETLIST,"$fname[$i]") or
    die "Can't open schematic $fname[$i]: $!\n";

  # open output netlist
  $outfname="$fname[$i].new";
  open(OUTSCH,">$outfname") or die "Can't open $outfname: $!\n";

    while($line = <NETLIST>) {
	$line = executeRENPART($line);
	print OUTSCH "$line";
    }
    close(NETLIST);
    close(OUTSCH);

    if( $nocopy ) {
      print "Leaving page #",$i+1," output in $outfname\n";
    }
    else {
      system("mv $outfname $fname[$i]");
    }

}

print "\n---- Gate Swapping ----\n";
executeSWPGATES();
print "\n---- Pin  Swapping ----\n";
executeSWPPINS();
print "\nBackannotation finished ", scalar localtime, "\n";

exit;


#######################################################################
#
# Subroutines
#
#######################################################################

#---------------------------------
# executeRENPART(line)
#---------------------------------

sub executeRENPART {
  my $line = shift(@_);

  return $line unless defined %cmd_rename;
  return $line unless $line =~ /^refdes=/;

  # pick out the reference designator
  $refdes = $line;
  $refdes =~ s/^refdes=//;
  $refdes =~ s/[\r\n]*$//;

  # see if its listed in our hash of reference designators to be
  # renamed
  return $line unless exists $cmd_rename{$refdes};

  print "executeRENPART():  Renaming $refdes to $cmd_rename{$refdes}\n" 
      if $verbose;
  return "refdes=$cmd_rename{$refdes}\n";

}

#---------------------------------
# executeSWPGATES()
#---------------------------------

sub executeSWPGATES {
  my $key;

  foreach $key (keys %cmd_swap_gates ) {
    print "Please manually swap gates $key and  $cmd_swap_gates{$key}\n";
  }
}

#---------------------------------
# executeSWPPINS()
#---------------------------------

sub executeSWPPINS {
  my $key;
  my @pins;

  foreach  $key (keys %cmd_swap_pins ) {
    @pins = split '\.',,$cmd_swap_pins{$key};
    print "Please manually swap pins $pins[0] and $pins[1] on $key\n";
  }
}

#---------------------------------
# parseRENPART(line)
#---------------------------------

sub parseRENPART {
  my $line = shift(@_);
  my @refs;
  @refs = split ' ',,$line;

  print "parseRENPART():  Scheduling rename of $refs[0] to $refs[1]\n" 
      if $verbose;
  $cmd_rename{$refs[0]} = $refs[1];

}

#---------------------------------
# parseSWPGATES(line)
#---------------------------------

sub parseSWPGATES {
  my $line = shift(@_);
  my @refs;
  @refs = split ' ',,$line;

  print "parseSWPGATES():  Scheduling swap of gate $refs[0] with $refs[1]\n" 
      if $verbose;
  $cmd_swap_gates{$refs[0]} = $refs[1];

}

#---------------------------------
# parseSWPPINS(line)
#---------------------------------

sub parseSWPPINS {
  my $line = shift(@_);
  @refs = split ' ',,$line;
  @pins = split '\.',,$refs[1];

  print "parseSWPPINS():  Scheduling swap of pins on $refs[0] : pins ",
  "$pins[0] and $pins[1]\n" if $verbose;
  $cmd_swap_pins{$refs[0]} = $refs[1];

}

#---------------------------------
# usage()
#
# prints program usage
#---------------------------------

sub usage {
  my $pname = $0;
  $pname =~ s/.*\///g;

  print "Usage:\n\n";
  print "\t$pname [-n|--nocopy] [-v|--verbose] change.eco file1.sch [file2.sch [file3.sch ... ] ]\n";
  print "\t$pname -h|--help\n";
  print "\t$pname -V|--version\n";
  print "\n";
  print "$pname reads a PADS PowerPCB ECO file and backannotates changes\n";
  print "to a gEDA schematic.\n";
  print "\n";
  print "$pname accepts the following options:\n";
  print "\n";
  print "    -h|--help      Displays this help message.\n";
  print "\n";
  print "    -n|--nocopy    If given, this flag leaves the modified files in new files\n";
  print "                whose names are generated by appending a \".new\" to the\n";
  print "                original file names.  The default is to overwrite the original.\n";
  print "\n";
  print "    -v|--verbose   Enables verbose output.\n";
  print "\n";
  print "    -V|--version   Shows the version of this program.\n";
  print "\n\n";
  print "$pname was written by Dan McMahill <dmcmahill\@netbsd.org>\n";
  print "\n\n";
  exit;
}

#---------------------------------
# version()
#
# prints program version
#---------------------------------

sub version {
  open(PROG,"$0") or die "Could not open \"$0\" to find version\n\n";
  my $pname = $0;
  $pname =~ s/.*\///g;

  while($line = <PROG>) {
    if( $line =~ /^#\s*\$Id.*\$/) {
      @words = split ' ',,$line;
      $version = $words[3];
      $date = $words[4];
      print "$pname ($0):  Version $version, $date\n";
      exit;
    }
  }
  print "Could not determine version of \"$0\"\n\n";
  exit;
}

# ----------------------------------------------
#
# Change Log
#
# $Log$
# Revision 1.1  2003-08-31 21:39:52  ahvezda
# Added pads_backannotate perl script
#
# Revision 1.4  2003/07/19 04:34:55  dan
# bring this program up to a usable state.  Refdes renumbering is fully
# supported and pin/gate swapping while not supported, gives a report of
# the manual swaps needed.
#
# Revision 1.3  2003/03/03 03:44:26  dan
# Finish up code to handle the RENPART .eco directive.  The output files
# are still not written, but everything else is in place to handle backannotation
# of reference designator renumbers.
#
# Revision 1.2  2003/02/21 12:30:30  dan
# fix perl path
#
# Revision 1.1  2003/02/19 23:31:50  dan
# start (non functional so far) of a pads to gschem backannotation tool
#
#
#
